package com.sparrow.common.util;

import java.util.Random;

/**
 * PRD（伪随机分布）概率触发器
 * 输入一个目标触发率 ratio（0 ~ 1），确保触发在随机性与分布均匀性之间取得平衡
 */
public class PRDRatioTrigger {

    private final double ratio;   // 期望的触发概率（如 0.25 = 25%）
    private final double c;       // 增量系数，自动通过 ratio 拟合计算
    private int failureCount = 1; // 连续未触发次数
    private final Random random;

    public PRDRatioTrigger(double ratio) {
        this(ratio, new Random());
    }

    public PRDRatioTrigger(double ratio, Random random) {
        if (ratio <= 0 || ratio >= 1) {
            throw new IllegalArgumentException("Ratio must be in (0, 1)");
        }
        this.ratio = ratio;
        this.c = calculateC(ratio);
        this.random = random;
    }

    /**
     * 尝试触发一次
     * @return true 表示触发；false 表示未触发
     */
    public boolean shouldTrigger() {
        double currentChance = c * failureCount;
        if (currentChance >= 1.0 || random.nextDouble() < currentChance) {
            failureCount = 1;
            return true;
        } else {
            failureCount++;
            return false;
        }
    }

    /**
     * 手动重置（可选）
     */
    public void reset() {
        failureCount = 1;
    }

    public int getFailureCount() {
        return failureCount;
    }

    public double getC() {
        return c;
    }

    public double getRatio() {
        return ratio;
    }

    // ========== 以下为 C 值拟合核心算法 ==========

    private double calculateC(double ratio) {
        double low = 0.0;
        double high = ratio;
        double mid = ratio;
        double lastTested = 1.0;

        while (true) {
            mid = (low + high) / 2.0;
            double tested = averageRatio(mid);

            if (Math.abs(tested - lastTested) <= 0.00005) {
                break;
            }

            if (tested > ratio) {
                high = mid;
            } else {
                low = mid;
            }

            lastTested = tested;
        }

        return mid;
    }

    private double averageRatio(double c) {
        double expected = 0.0;
        double acc = 0.0;
        int nMax = (int) Math.ceil(1.0 / c);
        for (int i = 1; i <= nMax; i++) {
            double p = Math.min(1.0, i * c) * (1 - acc);
            acc += p;
            expected += i * p;
        }
        return 1.0 / expected;
    }

    public static void main(String[] args) {
        PRDRatioTrigger trigger = new PRDRatioTrigger(0.25); // 25% 概率

        for (int i = 1; i <= 20; i++) {
            boolean triggered = trigger.shouldTrigger();
            System.out.printf("Try %2d => %s\n", i, triggered ? "TRIGGERED" : "miss");
        }
    }
}

